﻿using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;

namespace TranslatorService
{
	/// <summary>
	/// Static methods to help with Http calls to methods
	/// </summary>
	public static class HttpUtility
	{
		/// <summary>
		/// Builds the URL from a base, method name, and name/value paired parameters. All parameters are encoded.
		/// </summary>
		/// <param name="baseUrl">The base URL.</param>
		/// <param name="methodName">Name of the method.</param>
		/// <param name="pairs">The name/value paired parameters.</param>
		/// <returns>Resulting URL.</returns>
		/// <exception cref="System.ArgumentException">There must be an even number of strings supplied for parameters.</exception>
		public static string BuildUrl(string baseUrl, string methodName, params string[] pairs)
		{
			if (pairs.Length % 2 != 0)
				throw new ArgumentException("There must be an even number of strings supplied for parameters.");

			StringBuilder sb = new StringBuilder(baseUrl);
			if (!string.IsNullOrWhiteSpace(methodName))
			{
				if (!baseUrl.EndsWith("/"))
					sb.Append("/");
				sb.Append(methodName);
			}
			if (pairs != null && pairs.Length > 0)
			{
				sb.Append("?");
				var sl = new List<string>(pairs.Length / 2);
				for (int i = 0; i < pairs.Length; i += 2)
					sl.Add(HttpUtility.UrlEncode(pairs[i]) + "=" + HttpUtility.UrlEncode(pairs[i + 1]));
				sb.Append(string.Join("&", sl.ToArray()));
			}
			return sb.ToString();
		}

		/// <summary>
		/// Encodes a string for use in a URL.
		/// </summary>
		/// <param name="s">The string to encode.</param>
		/// <returns>Encoded string.</returns>
		public static string UrlEncode(string s)
		{
			return Uri.EscapeDataString(s).Replace("%20", "+");
		}

		/// <summary>
		/// Calls a web service method asynchronously.
		/// </summary>
		/// <typeparam name="T">The expected <see cref="Type"/> of the result.</typeparam>
		/// <param name="baseUrl">The base URL.</param>
		/// <param name="methodName">Name of the method.</param>
		/// <param name="authHeader">Optional string to include in the "Authorization" request header.</param>
		/// <param name="parameters">The name/value paired parameters.</param>
		/// <returns>The method result cast to the type defined by <typeparamref name="T"/>.</returns>
		public static async Task<T> CallMethodAsync<T>(string baseUrl, string methodName, string authHeader, params string[] parameters) where T : class
		{
			var dcs = new DataContractSerializer(typeof(T));
			return await CallSerializedMethodAsync<T>(s => (T)dcs.ReadObject(s), baseUrl, methodName, authHeader, parameters);
		}

		/// <summary>
		/// Calls a web service method whose results are in JSON format asynchronously.
		/// </summary>
		/// <typeparam name="T">The expected <see cref="Type"/> of the result.</typeparam>
		/// <param name="baseUrl">The base URL.</param>
		/// <param name="methodName">Name of the method.</param>
		/// <param name="authHeader">Optional string to include in the "Authorization" request header.</param>
		/// <param name="parameters">The name/value paired parameters.</param>
		/// <returns>The method result cast to the type defined by <typeparamref name="T"/>.</returns>
		public static async Task<T> CallJsonMethodAsync<T>(string baseUrl, string methodName, string authHeader, params string[] parameters) where T : class
		{
			if (typeof(T) == typeof(string))
				return await CallSerializedMethodAsync<string>(s => new System.IO.StreamReader(s).ReadToEnd(), baseUrl, methodName, authHeader, parameters) as T;

			DataContractJsonSerializer dcs = new DataContractJsonSerializer(typeof(T));
			return await CallSerializedMethodAsync<T>(s => (T)dcs.ReadObject(s), baseUrl, methodName, authHeader, parameters);
		}

		private static async Task<T> CallSerializedMethodAsync<T>(Func<System.IO.Stream, T> serialize, string baseUrl, string methodName, string authHeader, params string[] parameters) where T : class
		{
			string url = BuildUrl(baseUrl, methodName, parameters);
			using (HttpClient c = new HttpClient())
			{
				if (!string.IsNullOrWhiteSpace(authHeader))
					c.DefaultRequestHeaders.Add("Authorization", authHeader);

				try
				{
					using (var stream = await c.GetStreamAsync(url))
						return serialize(stream);
				}
				catch (Exception ex)
				{
					System.Diagnostics.Debug.WriteLine("Failure: " + url + "\r\n" + ex.Message);
				}
				return default(T);
			}
		}
	}
}
